/** \file log.hpp
 * \verbatim
 * liblog C++ wrapper.
 * 
 * Copyright (c) 2001-2003 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 *
 * \par Example:
 * \code
 * CLog clg;
 * int main(int argc, char **argv) {
 *   CLogThread MainThread("main");
 * 
 *   clg.err.Edit(LOG_GROUP_SET_LEVEL);
 *   clg.debug() << HDR << "Reading command-line argument" << endl;
 *   if (argc < 2)
 *   {
 *     clg.err() << "Too few arguments" << endl;
 *     return -1;
 *   }
 *   ...
 * }
 * \endcode
 */

#ifndef __LOG_HPP_INCLUDED
#define __LOG_HPP_INCLUDED

#if defined (__GNUC__) && (__GNUC__ < 3)
#include <lsstream>
#else
#include <sstream>
#endif /* __GNUC__ */
#include <string>
#include <log.h>

/// Encapsulation of information needed for the logheader.
#define HDR CLogHeader(__FILE__, __LINE__, LOG_FUNC)

/** \brief Defines a thread object for logging purposes.
 *
 *  Can be instantiated at the beginning of a thread main-function
 *  to provide the name of the thread to the logging system.
 */
class CLogThread
{
  public:
    /** \brief Registers the thread in the logging system.
     *  \param s Thread name.
     */
    CLogThread(const char *s)
    {
      logRegisterThread(s);
    }

    /// Unregister the thread
    ~CLogThread()
    {
      logUnRegisterThread();
    }
};

/// Encapsulates the information needed for the logheader.
class CLogHeader
{
  public:
    const char *file;               ///< Source file.
    int line;                       ///< Current line.
    const char *function;           ///< Current function.

    CLogHeader(const char *_file, int _line, const char *_function) :
      file(_file), line(_line), function(_function) { }
};

/** \brief Logging message buffer.
 *
 *  Note that in general use, a CLogMessage object has only a very
 *  short lifespan: the creation of the log message. This is
 *  because thread safety requires a new object to be used for
 *  every message. Upon destruction, this object will then
 *  output its message.
 *
 *  Note that the operator<< for the header is only defined on
 *  CLogMessage, and not on ostringstream. This means that the header
 *  can only appear at the start of the line.
 */
class CLogMessage
{
  private:
    int lstream;                    ///< Index in stream table.
    std::ostringstream buf;         ///< Temporary message buffer.

  public:
    /** \param _lstream Index in stream table. */
    CLogMessage(int _lstream) : lstream(_lstream) { }

    CLogMessage(const CLogMessage &o) : lstream(o.lstream) { }

    /// Output the gathered information.
    ~CLogMessage()
    {
      logOut(lstream, const_cast<char*>(this->buf.str().c_str()));
    }

    /** \brief Add a header to the log message.
     *  \param header Encapsulated header information.
     */
    std::ostringstream& operator<<(const CLogHeader &hdr)
    {
      char _buf[256];

      logPrintHeader(hdr.file, hdr.line, hdr.function, lstream, _buf);
      buf << _buf;
      return buf;
    }

    /** \brief Catch-all operator for classes other than CLogHeader.
     *  \param o object of class T to insert into stream.
     */
    template<class T>
    std::ostringstream& operator<<(T &o)
    {
      buf << o;
      return buf;
    }
};

/** \brief Logging message stream.
 *
 *  This is not a stream in the sense that it can be used for
 *  output. Rather, it can spawn message buffers using the
 *  () operator, and these message buffers accept << operations. 
 */
class CLogStream
{
  private:
    int lstream;                    ///< Index in stream table.

  public:
    CLogStream() : lstream(0) { }

    /** \param _lstream Index in stream table. */
    CLogStream(int _lstream) : lstream(_lstream) { }

    /** \brief Returns a temporary CLogMessage object for a single
     *  message.
     */
    CLogMessage operator()()
    {
      CLogMessage buf(lstream);
      return buf;
    }

    /** \brief Sets the stream index to log to.
     * \param _lstream Index in stream table.
     */
    void SetStream(int _lstream)
    {
      lstream = _lstream;
    }

    /** \brief Gets the stream index this stream logs to.
     *  \returns Index in stream table.
     */
    int GetStream()
    {
      return lstream;
    }

    /** \brief Change some stream option
     *  \param operation Operation to perform on the stream or group.
     *
     *  See the documentation of \c logEdit for possible operations.
     */
    int Edit(int operation)
    {
      return logEdit(lstream, operation);
    }
    
    /** \brief Shorthand for Edit(LOG_STREAM_GET_ENABLED) */
    int IsEnabled(void)
    {
      return Edit(LOG_STREAM_GET_ENABLED);
    }
};

/** \brief Defines a default log class.
 *  
 *  This default log class contains liblog's default streams, with
 *  their default settings. If you want other streams, use this class
 *  as an example to define your own.
 */
class CLog
{
  public:
    CLogStream crit,
               err,
               warning,
               notice,
               info,
               debug,
               crawl;

  public:
    /** \param name Name of the logging group, defaulting to the
     *  program-default group "default".
     *  \param file File to log to, defaulting to stdout/stderr.
     *
     *  If the logging group does not yet exist, it is created,
     *  pointing to \c file. If the logging group is \c NULL, it
     *  it always created.
     */ 
    CLog(const char *name="default", const char *file=NULL)
    {
#ifndef LIBDYNAMIC
	  liblog_init();
#endif
      Construct(name, file);
    }

    /** \brief Reconstruct logging streams after transport.
     *
     *  When transporting a CLog object from one address space to
     *  the next, the stream identifiers may change. The
     *  reconstruct operator can be used to reconnect the streams.
     *  Note that this is unnecessary for objects using the "default"
     *  group.
     *  \see CLog::CLog
     */
    void Reconstruct(const char *name="default", const char *file=NULL)
    {
      Construct(name, file);
    }

  private:
    /** \brief Internal construction method.
     *  \see CLog::CLog
     */
    void Construct(const char *name, const char *file)
    {
      int s;

      /* Attach our streams */
      if (!name || (s = logGetFirstGroupStream(name)) < 0)
      {
        /* New streams */
        logAddNamedGroup(name);
        log_default_streams_t streams = logAddDefaultStreams(file);

        crit.SetStream(streams.crit);
        err.SetStream(streams.err);
        warning.SetStream(streams.warning);
        notice.SetStream(streams.notice);
        info.SetStream(streams.info);
        debug.SetStream(streams.debug);
        crawl.SetStream(streams.crawl);
      }
      else
      {
        /* Old streams */
        crit.SetStream(s);
        err.SetStream(s = logGetNextGroupStream(s));
        warning.SetStream(s = logGetNextGroupStream(s));
        notice.SetStream(s = logGetNextGroupStream(s));
        info.SetStream(s = logGetNextGroupStream(s));
        debug.SetStream(s = logGetNextGroupStream(s));
        crawl.SetStream(s = logGetNextGroupStream(s));
      }      
    }

};

#endif /* __LOG_HPP_INCLUDED */
